﻿using System;
using System.Collections.Generic;

namespace UnityGameFramework.Runtime
{
    /// <summary>
    /// 比较两个实例是否相等。
    /// </summary>
    public delegate bool EqualityComparison<in T>(T x, T y);

    /// <summary>
    /// 属性值变更事件处理委托。
    /// </summary>
    public delegate void PropertyValueChangeHandler<in T>(T value, T previousValue);

    /// <summary>
    /// 可监听值变动的属性。
    /// </summary>
    public sealed class ObservableProperty<T> : IDisposable
    {
        /// <summary>
        /// 存储的值。修改该值将触发值更新事件。
        /// </summary>
        public T Value
        {
            get => m_Value;
            set
            {
                if (m_Comparison != null && m_Comparison(m_Value, value))
                {
                    // 值相同
                    return;
                }

                if (m_OnValueChange != null)
                {
                    var previousValue = m_Value;
                    m_Value = value;
                    m_OnValueChange(m_Value, previousValue);
                }
                else
                {
                    m_Value = value;
                }
            }
        }

        private T m_Value;
        private PropertyValueChangeHandler<T> m_OnValueChange;

        private readonly T m_DefaultValue;
        private readonly EqualityComparison<T> m_Comparison;

        /// <summary>
        /// 使用默认的比较函数构造可监听属性。
        /// </summary>
        /// <param name="value">初始值。</param>
        public ObservableProperty(T value = default) : this(value, DefaultComparison)
        {
        }

        /// <summary>
        /// 使用指定的初始值和比较函数构造可监听属性。
        /// </summary>
        /// <param name="value">初始值。</param>
        /// <param name="comparison">比较函数。若为null则代表不做值比较，每次都进行赋值。</param>
        public ObservableProperty(T value, EqualityComparison<T> comparison)
        {
            m_DefaultValue = value;
            m_Value = value;
            m_Comparison = comparison;
        }

        /// <summary>
        /// 订阅值变更事件。
        /// </summary>
        public void Subscribe(PropertyValueChangeHandler<T> handler, bool invokeImmediately = false)
        {
            m_OnValueChange += handler;
            if (invokeImmediately)
            {
                handler(m_Value, m_Value);
            }
        }

        /// <summary>
        /// 订阅值变更事件。并返回可以销毁的句柄。
        /// </summary>
        public IDisposable SubscribeDisposable(PropertyValueChangeHandler<T> handler, bool invokeImmediately = false)
        {
            Subscribe(handler, invokeImmediately);
            return new SubscribeDisposableHandle(this, handler);
        }

        /// <summary>
        /// 取消订阅值变更事件。
        /// </summary>
        public void Unsubscribe(PropertyValueChangeHandler<T> handler)
        {
            m_OnValueChange -= handler;
        }

        /// <summary>
        /// 清理属性数据。
        /// </summary>
        public void Dispose()
        {
            m_OnValueChange = null;
            m_Value = m_DefaultValue;
        }

        /// <summary>
        /// 默认的比较函数。
        /// </summary>
        private static bool DefaultComparison(T x, T y)
        {
            return EqualityComparer<T>.Default.Equals(x, y);
        }

        /// <summary>
        /// 可销毁的订阅辅助器。
        /// </summary>
        private readonly struct SubscribeDisposableHandle : IDisposable
        {
            private readonly ObservableProperty<T> m_ObservableProperty;
            private readonly PropertyValueChangeHandler<T> m_Handler;

            public SubscribeDisposableHandle(ObservableProperty<T> observableProperty, PropertyValueChangeHandler<T> handler)
            {
                m_ObservableProperty = observableProperty;
                m_Handler = handler;
            }
            
            public void Dispose()
            {
                m_ObservableProperty.Unsubscribe(m_Handler);
            }
        }

        public static implicit operator T(ObservableProperty<T> property)
        {
            return property.Value;
        }
    }
}